//
//  NSDictionary+ZXAvoidCrash.m
//  ZXAvoidCrash
//
//  Created by 朱新明 on 2023/2/3.
//

#import "NSDictionary+ZXAvoidCrash.h"
#import "ZXAvoidCrashRuntime.h"
#import "ZXAvoidCrashReport.h"

@implementation NSDictionary (ZXAvoidCrash)

+ (void)zx_avoidCrashExchangeMethod{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        //NSDictionary和NSMutableDictionary的占位对象：instance method 和 类方法同一个;alloc创建的对象。
        Class __NSPlaceholderDictionary = NSClassFromString(@"__NSPlaceholderDictionary");
        
        [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSPlaceholderDictionary originalSelector:@selector(initWithObjects:forKeys:count:) swizzledSelector:@selector(zx_avoidCrashInitWithObjects:forKeys:count:)];
        
        
        Class NSDictionary = NSClassFromString(@"NSDictionary");
        [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:NSDictionary originalSelector:@selector(initWithObjects:forKeys:) swizzledSelector:@selector(zx_avoidCrashInitWithObjects:forKeys:)];
    });
}

#pragma mark- 初始化

//=================================================================
/*
 __NSPlaceholderDictionary
*  1. 字面值创建(这种创建方式底层就是调用2中的方法)：
   id someValue = nil;
   NSDictionary *dic = @{@"1":someValue};
*  2.- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects forKeys:(const KeyType <NSCopying> _Nonnull [_Nullable])keys count:(NSUInteger)cnt; //返回nil
*  3. + (instancetype)dictionaryWithObjects:(const ObjectType _Nonnull [_Nullable])objects forKeys:(const KeyType <NSCopying> _Nonnull [_Nullable])keys count:(NSUInteger)cnt;
*  4.+ (instancetype)dictionaryWithObject:(ObjectType)object forKey:(KeyType <NSCopying>)key;
 崩溃：
 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'
 */

- (instancetype)zx_avoidCrashInitWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt
{
    id instance = nil;
    @try {
        instance =  [self zx_avoidCrashInitWithObjects:objects forKeys:keys count:cnt];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
        //以下是对错误数据的处理:objects肯定不为NULL，是一个指针，objects内部元素有nil时会崩溃。过滤nil对象，如果无对象返回空字典。
        NSUInteger index = 0;
        id  _Nonnull __unsafe_unretained newObjects[cnt];
        id  _Nonnull __unsafe_unretained newkeys[cnt];
        for (int i = 0; i < cnt; i++) {
            if (objects[i] && keys[i]) {
                newObjects[index] = objects[i];
                newkeys[index] = keys[i];
                index++;
            }
        }
        instance =  [self zx_avoidCrashInitWithObjects:newObjects forKeys:newkeys count:index];
    } @finally {
        return instance;
    }
}


/*
 -[NSDictionary initWithObjects:forKeys:]: count of objects (2) differs from count of keys (1)
 */
- (instancetype)zx_avoidCrashInitWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
{
    id instance = nil;
    @try {
        instance = [self zx_avoidCrashInitWithObjects:objects forKeys:keys];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return instance;
    }
}

@end
